package fr.irit.etracetool.query;

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;

import fr.irit.etracetool.eobjectgraph.BipartiteGraph;
import fr.irit.etracetool.etrace.AbstractLink;
import fr.irit.etracetool.etrace.CompositeLink;
import fr.irit.etracetool.etrace.EObjectOrigin;
import fr.irit.etracetool.etrace.ETrace;
import fr.irit.etracetool.etrace.Link;


public class Query extends Predicate {
	public Query(Resource source, Resource target, ETrace trace) {
		super(source, target, trace);
	}
	
	/**
	 * Test if an Object is in a source feature of a trace link.
	 * @param eo : the object to test
	 * @return true if eo is referenced by a source feature of a trace link.
	 */
	public boolean inTraceSource(EObject eo) {
		return contains(eo, trace.getAllSources());
	}
	
	/**
	 * Test if an Object is in a target feature of a trace link.
	 * @param eo : the object to test
	 * @return true if eo is referenced by a target feature of a trace link.
	 */
	public boolean inTraceTarget(EObject eo) {
		return contains(eo, trace.getAllTargets());
	}
	
	/**
	 * Test if an Object is in a target or a source feature of a trace link.
	 * @param eo : the object to test
	 * @return inTraceSource(eo) || inTraceTarget(eo)
	 */
	public boolean inTrace(EObject eo) {
		return contains(eo, trace.getAllReferencedElements());
	}

	/** 
	 * @return the set of source model elements which not present in a source feature of a Link.
	 */
	public Set<EObject> nonTracedSourceElement() {
		HashSet<EObject> hs = new HashSet<EObject>();
		for (EObject e: sourceContents) {
			if (!inTraceSource(e)) {
				hs.add(e);
			}
		}
		return hs;
	}
	
	/** 
	 * @return the set of target model elements which not present in a target feature of a Link.
	 */
	public Set<EObject> nonTracedTargetElement() {
		HashSet<EObject> hs = new HashSet<EObject>();
		for (EObject e: targetContents) {
			if (!inTraceTarget(e)) {
				hs.add(e);
			}
		}
		return hs;
	}
	/**
	 * @return the set of sources of trace links which not present in the source model
	 */
	public Set<EObject> externalSource() {
		HashSet<EObject> hs = new HashSet<EObject>();
		for (EObject e: trace.getAllSources()) {
			if (!sourcePredicate(e)) {
				hs.add(e);
			}
		}
		return hs;
	}
	
	/**
	 * @return the set of target of trace links which not present in the target model
	 */
	public Set<EObject> externalTarget() {
		HashSet<EObject> hs = new HashSet<EObject>();
		for (EObject e: trace.getAllTargets()) {
			if (!targetPredicate(e)) {
				hs.add(e);
			}
		}
		return hs;
	}
	
	public float rapportNombreDeFeuilleSurNombreDeLiensTotal() {
		float nbComposite = (float) trace.numberOfCompositeLinks();
		float nbLink = (float) trace.numberOfLinks();
		return nbLink / (nbComposite + nbLink);

	}
	
	public float rapportNombreDeCompositeSurNombreDeLiensTotaux() {
		float nbComposite = (float) trace.numberOfCompositeLinks();
		float nbLink = (float) trace.numberOfLinks();
		return nbComposite / (nbComposite + nbLink);
	}
	
	public float nombreMoyenDeFeuilleParLienComposite() {
		int total = 0;
		for (CompositeLink cl : trace.getAllCompositeLinks()) {
			for (AbstractLink al : cl.getChildren()) {
				if (al instanceof Link) {
					total++;
				}
			}
		}
		return ((float)total) / ((float)trace.getAllCompositeLinks().size());
	}

	public Set<Link> targetToTargetLink() {
		HashSet<Link> hs = new HashSet<Link>();
		for (Link l: trace.getAllLinks()) {
			if (targetPredicate(l.getSource()) && targetPredicate(l.getTarget())) {
				hs.add(l);
			}
		}
		return hs;
	}
	
	public double percentSourceToTargetLink() {
		return (float) sourceToTargetLink().size() / (trace.getAllLinks().size()) *100;
	}

	public double percentSourceToSource() {
		return (float) sourceToSourceLink().size() / (trace.getAllLinks().size()) *100;
	}
	
	public double percentSourceToIntermediate() {
		return (float) sourceToIntermediateObjectLink().size() / (trace.getAllLinks().size()) *100;
	}

	public float percentTargetToIntermediateObjectLink() {
		return (float) targetToIntermediateObjectLink().size() / (trace.getAllLinks().size()) *100;
	}
	
	public float percentIntermediateToIntermediateObjectLink() {
		return (float)intermediateObjectToIntermediateObjectLink().size() / (trace.getAllLinks().size()) *100;
	}
	
	public float percentIntermediateToTargetLink() {
		return intermediateObjectToTargetLink().size() / (trace.getAllLinks().size()) *100;
	}
	
	public float percentTargetToTargetLink() {
		return targetToTargetLink().size() / (trace.getAllLinks().size()) *100;
	}
	
	
	public Set<Link> sourceToSourceLink() {
		HashSet<Link> hs = new HashSet<Link>();
		for (Link l: trace.getAllLinks()) {
			if (sourcePredicate(l.getSource()) && sourcePredicate(l.getTarget())) {
				hs.add(l);
			}
		}
		return hs;
	}

	public Set<Link> sourceToIntermediateObjectLink() {
		HashSet<Link> hs = new HashSet<Link>();
		for (Link l: trace.getAllLinks()) {
			if (sourcePredicate(l.getSource()) && intermediatePredicate(l.getTarget())) {
				hs.add(l);
			}
		}
		return hs;
	}
	
	public Set<Link> sourceToTargetLink() {
		HashSet<Link> hs = new HashSet<Link>();
		for (Link l: trace.getAllLinks()) {
			if (sourcePredicate(l.getSource()) && targetPredicate(l.getTarget())) {
				hs.add(l);
			}
		}
		return hs;
	}
	
	public Set<Link> intermediateObjectToIntermediateObjectLink() {
		HashSet<Link> hs = new HashSet<Link>();
		for (Link l: trace.getAllLinks()) {
			if (intermediatePredicate(l.getSource()) && intermediatePredicate(l.getTarget())) {
				hs.add(l);
			}
		}
		return hs;
	}
	
	public Set<Link> intermediateObjectToTargetLink() {
		HashSet<Link> hs = new HashSet<Link>();
		for (Link l: trace.getAllLinks()) {
			if (intermediatePredicate(l.getSource()) && targetPredicate(l.getTarget())) {
				hs.add(l);
			}
		}
		return hs;
	}

	public Set<Link> targetToIntermediateObjectLink() {
		HashSet<Link> hs = new HashSet<Link>();
		for (Link l: trace.getAllLinks()) {
			if (targetPredicate(l.getSource()) && intermediatePredicate(l.getTarget())) {
				hs.add(l);
			}
		}
		return hs;
	}
	
	public static void printResult(Collection<EObject> c) {
		for (EObject e: c) {
			System.out.println(e);
		}
	}
	
	public static void printResult(Collection<EObject> c, String title) {
		System.out.println("Titre");
		Query.printResult(c);
	}
	
	public BipartiteGraph generateTransitivityGraph() {
		BipartiteGraph graph = new BipartiteGraph();
		initializeLinkType();
		Collection<Link> allLink = trace.getAllLinks();
		for (Link l : allLink) {
			if (l.getType().getSourceOrigin() == EObjectOrigin.SOURCE) {
				if (l.getTarget() != null && (l.getType().getSourceOrigin() != EObjectOrigin.INTERMEDIATE)) {
					graph.add(l.getSource(), l.getTarget());
					List<EObject> targetList = new LinkedList<EObject>();
					searchTargets(l.getTarget(), targetList, new LinkedList<EObject>());
					for (EObject target : targetList) {
						graph.add(l.getSource(), target);
					}
				}
			}
		}
		return graph;
	}

	private void searchTargets(EObject target, Collection<EObject> targetList, Collection<EObject> done) {
		Collection<Link> allLink = trace.getAllLinks();
		for (Link l : allLink) {
			if (EcoreUtil.equals(target, l.getSource())) {
				if (l.getTarget() != null) {
					targetList.add(l.getTarget());
					done.add(l.getTarget());
					if (!done.contains(l.getTarget())) {
						searchTargets(l.getTarget(), targetList, done);
					}
				}
			}
		}
	}
}
